package com.cblz.bd.server.core.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cblz.bd.common.core.constant.RedisConstant;
import com.cblz.bd.common.core.entity.BaAdmin;
import com.cblz.bd.common.core.entity.BaSecurityDataRecycle;
import com.cblz.bd.common.core.entity.BaSecurityDataRecycleLog;
import com.cblz.bd.common.core.exception.BdAdminException;
import com.cblz.bd.common.core.utils.BdAdminUtil;
import com.cblz.bd.common.core.utils.ReflectUtil;
import com.cblz.bd.redis.spring.boot.starter.service.RedisService;
import com.cblz.bd.server.core.annotation.Log;
import com.cblz.bd.server.core.entity.vo.BaSecurityDataRecycleLogVO;
import com.cblz.bd.server.core.mapper.BaSecurityDataRecycleLogMapper;
import com.cblz.bd.server.core.service.BaAdminService;
import com.cblz.bd.server.core.service.BaSecurityDataRecycleLogService;
import com.cblz.bd.server.core.service.BaSecurityDataRecycleService;
import io.vavr.Tuple3;
import io.vavr.control.Option;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yanjieping
 * @description 针对表【ba_security_data_recycle_log(数据回收记录表)】的数据库操作Service实现
 * @createDate 2022-11-04 15:09:53
 */
@Slf4j
@Service("baSecurityDataRecycleLogService")
@RequiredArgsConstructor
public class BaSecurityDataRecycleLogServiceImpl extends ServiceImpl<BaSecurityDataRecycleLogMapper, BaSecurityDataRecycleLog>
        implements BaSecurityDataRecycleLogService {

    private final RedisService redisService;
    private final BaAdminService baAdminService;
    private final ApplicationContext applicationContext;
    private final BaSecurityDataRecycleService baSecurityDataRecycleService;

    /**
     * 查询
     *
     * @param wrapperBaSecurityDataRecycleTuple3 搜索条件处理后的信息元祖
     * @return
     */
    @Override
    public Map<String, Object> index(Tuple3<Page<BaSecurityDataRecycleLog>, QueryWrapper<BaSecurityDataRecycleLog>, BaSecurityDataRecycleLog> wrapperBaSecurityDataRecycleTuple3) {
        Page<BaSecurityDataRecycleLog> logPage = wrapperBaSecurityDataRecycleTuple3._1;
        QueryWrapper<BaSecurityDataRecycleLog> queryWrapper = wrapperBaSecurityDataRecycleTuple3._2;

        Page<BaSecurityDataRecycleLog> page = this.page(logPage, queryWrapper);

        List<BaSecurityDataRecycleLogVO> securityDataRecycleLogVOS = page.getRecords().stream().map(baSecurityDataRecycleLog -> {
            BaSecurityDataRecycleLogVO baSecurityDataRecycleLogVO = BeanUtil.copyProperties(baSecurityDataRecycleLog, BaSecurityDataRecycleLogVO.class);

            BaAdmin baAdmin = baAdminService.getById(baSecurityDataRecycleLogVO.getAdminId());
            BaSecurityDataRecycle securityDataRecycle = baSecurityDataRecycleService.getById(baSecurityDataRecycleLogVO.getRecycleId());

            baSecurityDataRecycleLogVO.setAdmin(baAdmin);
            baSecurityDataRecycleLogVO.setRecycle(securityDataRecycle);
            return baSecurityDataRecycleLogVO;
        }).toList();

        return Map.of("list", securityDataRecycleLogVOS, "total", page.getTotal(), "remark", "删除功能为不可恢复操作，删除前请慎重确认！");
    }

    /**
     * 删除数据回收记录信息
     *
     * @param ids id集合
     */
    @Override
    @Log(description = "删除数据回收记录信息")
    public void delete(List<Serializable> ids) {
        removeBatchByIds(ids);

        // 表信息更改，删除Redis缓存
        String hashKey = "securityDataRecycleHashMap";
        String key = RedisConstant.SECURITY_DATA_RECYCLE_PREFIX.getValue() + hashKey;
        redisService.del(key);
    }

    /**
     * 根据ID拿到信息
     *
     * @param id id
     * @return
     */
    @Override
    public BaSecurityDataRecycleLogVO getInfoById(Integer id) {
        BaSecurityDataRecycleLog securityDataRecycleLog = Option.of(getById(id)).getOrElseThrow(() -> {
            throw new BdAdminException("找不到ID为「" + id + "」的信息");
        });

        BaSecurityDataRecycleLogVO baSecurityDataRecycleLogVO = BeanUtil.copyProperties(securityDataRecycleLog, BaSecurityDataRecycleLogVO.class);

        BaAdmin baAdmin = Option.of(baAdminService.getById(baSecurityDataRecycleLogVO.getAdminId())).getOrElseThrow(() -> {
            throw new BdAdminException("找不到ID为「" + id + "」的管理员");
        });
        BaSecurityDataRecycle securityDataRecycle = Option.of(baSecurityDataRecycleService.getById(baSecurityDataRecycleLogVO.getRecycleId())).getOrElseThrow(() -> {
            throw new BdAdminException("找不到ID为「" + id + "」的数据安全回收规则信息");
        });

        Map<String, Object> map = JSONUtil.parseObj(baSecurityDataRecycleLogVO.getData());

        baSecurityDataRecycleLogVO.setAdmin(baAdmin);
        baSecurityDataRecycleLogVO.setRecycle(securityDataRecycle);
        baSecurityDataRecycleLogVO.setDataMap(map);

        return baSecurityDataRecycleLogVO;
    }

    /**
     * 还原删除数据
     *
     * @param ids 数据删除日志id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void restoreByIds(List<Serializable> ids) {
        ids.stream().map(this::getById).forEach(baSecurityDataRecycleLog -> {
            JSONObject data = JSONUtil.parseObj(baSecurityDataRecycleLog.getData());
            // 查询安全配置表中的数据，拿到配置信息
            BaSecurityDataRecycle baSecurityDataRecycle = baSecurityDataRecycleService.getById(baSecurityDataRecycleLog.getRecycleId());
            //  反射创建serviceImpl实体对象
            Class<?> serviceImpl;
            try {
                serviceImpl = Class.forName(baSecurityDataRecycle.getServiceImplPackage() + BdAdminUtil.underscoreToCamel(baSecurityDataRecycle.getDataTable()) + "ServiceImpl");
            } catch (ClassNotFoundException e) {
                throw new BdAdminException("找不到路径为「 " + baSecurityDataRecycle.getServiceImplPackage() + BdAdminUtil.underscoreToCamel(baSecurityDataRecycle.getDataTable()) + "ServiceImpl 」的类");
            }
            Set<Method> methodSet = Arrays.stream(serviceImpl.getMethods()).filter(method -> StringUtils.equals(method.getName(), "getById")).collect(Collectors.toSet());
            methodSet.forEach(method -> {
                //在ApplicationContext中根据class取出已实例化的bean
                Object invoke = null;
                try {
                    invoke = method.invoke(applicationContext.getBean(serviceImpl), data.get(baSecurityDataRecycleLog.getPrimaryKey()).toString() + "_DataSecurityRestoreSuffix");
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
                // 如果为空就新增之前的数据
                if (Objects.isNull(invoke)) {
                    Arrays.stream(serviceImpl.getMethods()).filter(m -> StringUtils.equals(m.getName(), "save")).distinct().forEach(v ->
                            {
                                try {
                                    v.invoke(applicationContext.getBean(serviceImpl), data);
                                } catch (Exception e) {
                                    throw new RuntimeException(e);
                                }
                            }
                    );
                } else {
                    // 如果不为空则表示逻辑删除，那就把删除的状态改为1
                    restoreUpdate(invoke, serviceImpl);
                }
                changeRestoreStatusById(baSecurityDataRecycleLog.getId());
            });
        });
    }

    /**
     * 改变还原状态，更改为已还原
     *
     * @param id 日志id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeRestoreStatusById(Serializable id) {
        this.update(new UpdateWrapper<BaSecurityDataRecycleLog>().lambda().set(BaSecurityDataRecycleLog::getIsRestore, 1).eq(BaSecurityDataRecycleLog::getId, id));
    }

    /**
     * 还原->修改
     *
     * @param object      修改实体类对象
     * @param serviceImpl 调用service
     */
    @Transactional(rollbackFor = Exception.class)
    public void restoreUpdate(Object object, Class<?> serviceImpl) {
        Object newInvoke = ReflectUtil.getTarget(object, Map.of("restoreIdentification", "_DataSecurityRestoreSuffix"));
        Arrays.stream(serviceImpl.getMethods()).filter(m -> StringUtils.equals(m.getName(), "updateById")).distinct().forEach(v ->
                {
                    try {
                        v.invoke(applicationContext.getBean(serviceImpl), newInvoke);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
        );
    }
}




